Ruby 日記 41日目: instance_evalの引数とスコープ
次のプログラムを実行するとどうなりますか
code:gold/ex41/main.rb
m = Module.new
CONST = "Constant in Toplevel"
_proc = Proc.new do
CONST = "Constant in Proc"
end
m.instance_eval(<<-EOS)
CONST = "Constant in Module instance"
def const
CONST
end
EOS
m.module_eval(&_proc)
p m.const
選択肢:
例外が発生する
"Constant in Toplevel"と表示される
"Constant in Proc"と表示される
"Constant in Module instance"と表示される
解説:
参考になるのはこの辺りかな
code:sh
# ruby gold/ex41/main.rb
gold/ex41/main.rb:6: warning: already initialized constant CONST
gold/ex41/main.rb:3: warning: previous definition of CONST was here
"Constant in Module instance"
正解は「"Constant in Module instance"と表示される」だね
warningが出ているのは、3行目で定義されたCONSTを6行目のProcオブジェクト内で再定義している(実際の定義は17行目のmodule_evalでやってる)からだね。
/icons/hr.icon
instance_eval(expr, filename = "(eval)", lineno = 1) -> object
instance_eval {|obj| ... } -> object
オブジェクトのコンテキストで文字列 expr またはオブジェクト自身をブロックパラメータとするブロックを 評価してその結果を返します。
文字列 expr or オブジェクト自身をブロックパラメータとするブロック を評価してその結果を返す
オブジェクトのコンテキストで評価するとは評価中の self をそのオブジェクトにして実行するということです。
今回の問題では1行目で m = Module.new した m のこと
また、文字列 expr やブロック中でメソッドを定義すればそのオブジェクトの特異メソッドが定義されます。
今回の問題では m に対して特異メソッド const が定義されるってこと
ただし、ローカル変数だけは、文字列 expr の評価では instance_eval の外側のスコープと、ブロックの評価ではそのブロックの外側のスコープと、共有します。
今回は m.instance_eval に文字列を渡しているので CONST は instance_eval の外側のスコープと共有される
今回の問題の CONST は2回上書きされていることになるのかな?
いや、3行目の CONST = "Constant in Toplevel" から instance_eval 内部の CONST = "Constant in Module instance" に、一度だけ上書きされているってことか?
/icons/hr.icon
例えば instance_eval に渡す文字列内部での CONST の定義を消すと?
code:gold/ex41/sample01.rb
m = Module.new
CONST = "Constant in Toplevel"
_proc = Proc.new do
CONST = "Constant in Proc"
end
m.instance_eval(<<-EOS)
def const
CONST
end
EOS
m.module_eval(&_proc)
p m.const
code:sh
# ruby gold/ex41/sample01.rb
gold/ex41/sample01.rb:6: warning: already initialized constant CONST
gold/ex41/sample01.rb:3: warning: previous definition of CONST was here
"Constant in Proc"
module_evalのProcで定義された "Constant in Proc" が参照されている。
/icons/hr.icon
例えば、今回の問題において m.instance_eval に文字列ではなくブロックを渡した場合
code:gold/ex41/sample02.rb
m = Module.new
CONST = "Constant in Toplevel"
_proc = Proc.new do
CONST = "Constant in Proc"
end
m.instance_eval do
CONST = "Constant in Module instance"
def const
CONST
end
end
m.module_eval(&_proc)
p m.const
code:sh
# ruby gold/ex41/sample02.rb
gold/ex41/sample02.rb:10: warning: already initialized constant CONST
gold/ex41/sample02.rb:3: warning: previous definition of CONST was here
gold/ex41/sample02.rb:6: warning: already initialized constant CONST
gold/ex41/sample02.rb:10: warning: previous definition of CONST was here
"Constant in Proc"
3行目の CONST = "Constant in Toplevel"
instance_eval 内部の CONST = "Constant in Module instance"
m.module_eval(&_proc) で呼び出されたProc内部の CONST = "Constant in Proc"
という順で2回上書きされているのか
むずいよこの辺〜〜